#include <jni.h>
#include <string>

extern "C" {
#include "libavutil/avutil.h"
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/time.h>

}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_or_ffmpeg_FFmpegUtils_stringFromJNI(JNIEnv *env, jclass clazz) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_or_ffmpeg_FFmpegUtils_getFFmpegVersion(JNIEnv *env, jclass clazz) {
    return env->NewStringUTF(av_version_info());
}


extern "C"
JNIEXPORT jstring JNICALL
Java_com_or_ffmpeg_FFmpegUtils_clip(JNIEnv *env, jclass clazz, jstring inputPath, jstring outputPath, jint start_ms, jint end_ms) {
}

// 声明JNI接口方法
extern "C"
JNIEXPORT void JNICALL
Java_com_or_ffmpeg_FFmpegUtils_cutVideo2(JNIEnv *env, jclass clazz, jstring inputPathObj, jstring outputPathObj, jint startMs, jint endMs) {
    // 获取输入和输出的文件路径
    const char *inputPath = env->GetStringUTFChars(inputPathObj, nullptr);
    const char *outputPath = env->GetStringUTFChars(outputPathObj, nullptr);

    // 初始化网络，以便能够打开URL资源
    avformat_network_init();

    AVFormatContext * formatContext = avformat_alloc_context();
    //1、打开URL
    AVDictionary *opts = NULL;
    //设置超时3秒
    av_dict_set(&opts, "timeout", "3000000", 0);
    //强制指定AVFormatContext中AVInputFormat的。这个参数一般情况下可以设置为NULL，这样FFmpeg可以自动检测AVInputFormat。
    //输入文件的封装格式
//    av_find_input_format("avi")
//    ret为零 表示成功
    int ret = avformat_open_input(&formatContext, inputPath, NULL, &opts);

    if (ret == 0){
        true;
    }

}


// 声明JNI接口方法
extern "C"
JNIEXPORT void JNICALL
Java_com_or_ffmpeg_FFmpegUtils_cutVideo(JNIEnv *env, jclass clazz,
                                        jstring inputPathObj, jstring outputPathObj,
                                        jint startMs, jint endMs) {
    // 获取输入和输出的文件路径
    const char *inputPath = env->GetStringUTFChars(inputPathObj, nullptr);
    const char *outputPath = env->GetStringUTFChars(outputPathObj, nullptr);

    // 初始化网络，以便能够打开URL资源
    avformat_network_init();

    // 打开输入视频文件
    AVFormatContext *pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx, inputPath, nullptr, nullptr) != 0) {
        // 文件打开失败处理
        env->ReleaseStringUTFChars(inputPathObj, inputPath);
        env->ReleaseStringUTFChars(outputPathObj, outputPath);
        return;
    }

    // 获取输入视频流信息
    if (avformat_find_stream_info(pFormatCtx, nullptr) < 0) {
        // 无法获取流信息
        avformat_close_input(&pFormatCtx);
        env->ReleaseStringUTFChars(inputPathObj, inputPath);
        env->ReleaseStringUTFChars(outputPathObj, outputPath);
        return;
    }

    // 查找视频流
    int videoStream = -1;
    for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
            break;
        }
    }
    if (videoStream == -1) {
        // 没有找到视频流
        avformat_close_input(&pFormatCtx);
        env->ReleaseStringUTFChars(inputPathObj, inputPath);
        env->ReleaseStringUTFChars(outputPathObj, outputPath);
        return;
    }

    // 初始化输出文件上下文
    AVFormatContext *outFormatCtx = nullptr;
    avformat_alloc_output_context2(&outFormatCtx, nullptr, nullptr, outputPath);
    if (!outFormatCtx) {
        // 创建输出上下文失败
        avformat_close_input(&pFormatCtx);
        env->ReleaseStringUTFChars(inputPathObj, inputPath);
        env->ReleaseStringUTFChars(outputPathObj, outputPath);
        return;
    }

    // 复制视频流信息到输出上下文
    AVStream *outStream = avformat_new_stream(outFormatCtx, nullptr);
    avcodec_parameters_copy(outStream->codecpar, pFormatCtx->streams[videoStream]->codecpar);

    // 打开输出文件
    if (!(outFormatCtx->oformat->flags & AVFMT_NOFILE)) {
        if (avio_open(&outFormatCtx->pb, outputPath, AVIO_FLAG_WRITE) < 0) {
            // 输出文件打开失败
            avformat_free_context(outFormatCtx);
            avformat_close_input(&pFormatCtx);
            env->ReleaseStringUTFChars(inputPathObj, inputPath);
            env->ReleaseStringUTFChars(outputPathObj, outputPath);
            return;
        }
    }

    // 初始化解码器
    const AVCodec *codec = avcodec_find_decoder(pFormatCtx->streams[videoStream]->codecpar->codec_id);
    AVCodecContext *dec_ctx = avcodec_alloc_context3(codec);
    if (avcodec_parameters_to_context(dec_ctx, pFormatCtx->streams[videoStream]->codecpar) < 0 ||
        avcodec_open2(dec_ctx, codec, nullptr) < 0) {
        // 解码器上下文配置或打开失败
        avcodec_free_context(&dec_ctx);
        avformat_free_context(outFormatCtx);
        avformat_close_input(&pFormatCtx);
        env->ReleaseStringUTFChars(inputPathObj, inputPath);
        env->ReleaseStringUTFChars(outputPathObj, outputPath);
        return;
    }

    // 预分配视频帧和RGB帧
    AVFrame *frame = av_frame_alloc();
    AVFrame *frameRGB = av_frame_alloc();
    if (!frame || !frameRGB) {
        // 分配帧失败
        avcodec_free_context(&dec_ctx);
        avformat_free_context(outFormatCtx);
        avformat_close_input(&pFormatCtx);
        av_frame_free(&frame);
        av_frame_free(&frameRGB);
        env->ReleaseStringUTFChars(inputPathObj, inputPath);
        env->ReleaseStringUTFChars(outputPathObj, outputPath);
        return;
    }

    // 计算输出RGB图像所需的缓冲区大小
    int width = dec_ctx->width;
    int height = dec_ctx->height;
    int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, width, height, 1);
    uint8_t *buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
    if (!buffer) {
        // 分配缓冲区失败
        avcodec_free_context(&dec_ctx);
        avformat_free_context(outFormatCtx);
        avformat_close_input(&pFormatCtx);
        av_frame_free(&frame);
        av_frame_free(&frameRGB);
        av_free(buffer);
        env->ReleaseStringUTFChars(inputPathObj, inputPath);
        env->ReleaseStringUTFChars(outputPathObj, outputPath);
        return;
    }
    // 设置RGB帧的数据和行宽
    av_image_fill_arrays(frameRGB->data, frameRGB->linesize, buffer, AV_PIX_FMT_RGB24, width, height, 1);

    // 创建缩放上下文，用于颜色空间转换
    SwsContext *sws_ctx = sws_getContext(width, height, dec_ctx->pix_fmt,
                                         width, height, AV_PIX_FMT_RGB24,
                                         SWS_BILINEAR, nullptr, nullptr, nullptr);
    if (!sws_ctx) {
        // 创建缩放上下文失败
        avcodec_free_context(&dec_ctx);
        avformat_free_context(outFormatCtx);
        avformat_close_input(&pFormatCtx);
        av_frame_free(&frame);
        av_frame_free(&frameRGB);
        av_free(buffer);
        env->ReleaseStringUTFChars(inputPathObj, inputPath);
        env->ReleaseStringUTFChars(outputPathObj, outputPath);
        return;
    }

    // 处理视频帧
    int ret;
    AVPacket packet;
    while (av_read_frame(pFormatCtx, &packet) >= 0) {
        if (packet.stream_index == videoStream) {
            // 发送视频帧到解码器
            ret = avcodec_send_packet(dec_ctx, &packet);
            if (ret < 0) {
                // 发送失败处理
                break;
            }

            // 循环直到所有的解码输出完成
            while (ret >= 0) {
                ret = avcodec_receive_frame(dec_ctx, frame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    // 需要更多数据或解码完成
                    break;
                } else if (ret < 0) {
                    // 解码错误
                    break;
                }

                // 当前时间在指定剪辑范围内，进行颜色空间转换并输出帧
                if (startMs <= av_gettime() / 1000 && av_gettime() / 1000 <= endMs) {
                    sws_scale(sws_ctx, frame->data, frame->linesize, 0, height,
                              frameRGB->data, frameRGB->linesize);
                    av_interleaved_write_frame(outFormatCtx, &packet);
                }

                // 释放解码后的帧，准备接收新的帧
                av_frame_unref(frame);
            }
        }
        // 释放原始包数据
        av_packet_unref(&packet);
    }

    // 清理资源
    sws_freeContext(sws_ctx);
    avcodec_free_context(&dec_ctx);
    avformat_free_context(outFormatCtx);
    avformat_close_input(&pFormatCtx);
    av_frame_free(&frame);
    av_frame_free(&frameRGB);
    av_free(buffer);
    env->ReleaseStringUTFChars(inputPathObj, inputPath);
    env->ReleaseStringUTFChars(outputPathObj, outputPath);
}

